
#include <BFont>
#include "member_BScrollArea.h"
#include "member_BScrollBar.h"
#include "member_BWidget.h"

using namespace BWE;

member_BScrollArea::member_BScrollArea(BScrollArea* scrollArea)
{
	boss = scrollArea;
	areaScale.set(1, 1);
	areaAlign = Align_None;
	areaPolicy = Policy_Preferred;
	titleVisible = false;
	titleSize = -1;
	titleSpace = 0;
	alignment.ia_align = Align_LeftCenter;

	autoTop = true;
	autoLeft = true;
	autoRight = true;
	autoBottom = true;
	handleSize = 0;

	dirty = true;
	hoverTitle = false;
	scrollStep = 0.0f;
	horScrollHint = ScrollHint_Auto;
	verScrollHint = ScrollHint_Auto;

	horScrollBar = new BScrollBar("horizontal", boss);
	horScrollBar->setOrientation(Orientation_Horizontal);
	connect(horScrollBar, Signal_ValueChanged, &member_BScrollArea::slotHorizontalScrollValueChanged);
	connect(horScrollBar, Signal_MouseLeave, &member_BScrollArea::slotMouseLeaveHorizontalScrollBar);

	verScrollBar = new BScrollBar("vertical", boss);
	verScrollBar->setOrientation(Orientation_Vertical);
	connect(verScrollBar, Signal_ValueChanged, &member_BScrollArea::slotVerticalScrollValueChanged);
	connect(verScrollBar, Signal_MouseLeave, &member_BScrollArea::slotMouseLeaveVerticalScrollBar);

}
member_BScrollArea::~member_BScrollArea()
{

}

void member_BScrollArea::adjustRects()
{
	const BFont* titleFont = 0;
	if (titleStyle)
		titleFont = titleStyle->value(Value_Font);
	if (!titleFont)
		titleFont = boss->realStyle()->font();

	BPerch perch = boss->perch();
	if (autoTop && titleVisible)
	{
		perch.top() = boss->preferSize(Part_Top).height();
		int realTitleHeight = bMax(titleSize, iconSize.height());
		if (realTitleHeight < titleFont->size())
			realTitleHeight = titleFont->size();
		realTitleHeight += titleMargin.height();
		if (perch.top() < realTitleHeight)
			perch.top() = realTitleHeight;
	}
	boss->setPerch(perch);

	int w = boss->width();
	int h = boss->height();
	int b = boss->border();

	BRect titleRect = boss->rect(Part_Top);
	titleRect.top() += titleMargin.top();
	titleRect.left() += titleMargin.left();
	titleRect.right() -= titleMargin.right();
	titleRect.bottom() -= titleMargin.bottom();

	BRect iconArea(titleRect.x(), titleRect.y(), 0, titleRect.height());
	BSize realIconSize;
	BIcon* icon = boss->icon();
	if (icon)
	{
		iconArea.setWidth(titleRect.height());
		realIconSize = iconSize;
		if (BImage* image = icon->image(State_Normal))
		{
			if (realIconSize.width() < 1)
				realIconSize.width() = image->width();
			if (realIconSize.height() < 1)
				realIconSize.height() = image->height();
		}
		if (realIconSize.width() < 1)
			realIconSize.width() = titleRect.height();
		if (realIconSize.height() < 1)
			realIconSize.height() = titleRect.height();
		realIconSize = iconArea.wrap(realIconSize);
	}
	alignment.ia_iconSize = realIconSize;

	if (widget_member(boss)->t_title.size())
		alignment.ia_textSize = titleFont->textSize(widget_member(boss)->t_title);
	else
		alignment.ia_textSize = titleFont->textSize(widget_member(boss)->title);
	headerRect = alignment.adjust(titleRect);
}
Orientation member_BScrollArea::adjustRealArea()
{
	BRect temp = rawArea;
	temp += spreadSize;
	temp.top() = int(temp.top() * areaScale.y());
	temp.left() = int(temp.left() * areaScale.x());
	temp.right() = int(temp.right() * areaScale.x());
	temp.bottom() = int(temp.bottom() * areaScale.y());
	hintSize = temp.size();
	BRect crect = boss->centerRect();
	if (areaAlign)
	{
		BRect rect = crect.align(areaAlign, temp.size());
		if (areaAlign & Align_Horizontal)
			temp.setX(rect.x());
		if (areaAlign & Align_Vertical)
			temp.setY(rect.y());
	}
	else
	{
		int mx = 0;
		if (temp.width() <= crect.width())
		{
			if (temp.left() < crect.left())
				mx += crect.left() - temp.left();
			if (temp.right() > crect.right())
				mx += crect.right() - temp.right();
		}
		int my = 0;
		if (temp.height() <= crect.height())
		{
			if (temp.top() < crect.top())
				my += crect.top() - temp.top();
			if (temp.bottom() > crect.bottom())
				my += crect.bottom() - temp.bottom();
		}
		temp.move(mx, my);
	}
	if (temp.width() > crect.width())
	{
		if (temp.left() > crect.left())
			temp.move(crect.left() - temp.left(), 0);
		if (temp.right() < crect.right())
			temp.move(crect.right() - temp.right(), 0);
	}
	if (temp.height() > crect.height())
	{
		if (temp.top() > crect.top())
			temp.move(0, crect.top() - temp.top());
		if (temp.bottom() < crect.bottom())
			temp.move(0, crect.bottom() - temp.bottom());
	}
	if (areaPolicy.widthPolicy() > Policy_Fixed)
	{
		if (areaPolicy.widthPolicy() & Policy_Expanding && temp.width() < crect.width())
			temp.setWidth(crect.width());
		if (areaPolicy.widthPolicy() & Policy_Shrinking && temp.width() > crect.width())
			temp.setWidth(crect.width());
	}
	if (areaPolicy.heightPolicy() > Policy_Fixed)
	{
		if (areaPolicy.heightPolicy() & Policy_Expanding && temp.height() < crect.height())
			temp.setHeight(crect.height());
		if (areaPolicy.heightPolicy() & Policy_Shrinking && temp.height() > crect.height())
			temp.setHeight(crect.height());
	}
	Orientation orientation = Orientation_None;
	if (area != temp)
	{
		if (area.x() != temp.x() || area.width() != temp.width())
			orientation = Orientation_Horizontal;
		if (area.y() != temp.y() || area.height() != temp.height())
			orientation |= Orientation_Vertical;
		area = temp;
		boss->emit(Signal_AreaChanged, area);
	}
	return orientation;
}
void member_BScrollArea::adjustScrollBar()
{
	const BRect& crect = boss->centerRect();
	int horStep = (int)scrollStep;
	int verStep = (int)scrollStep;
	if (scrollStep == 0.0f)
	{
		horStep = boss->realFont()->size();
		verStep = boss->realFont()->size();
	}
	else if (scrollStep < 1.0f)
	{
		horStep = int(area.width() * scrollStep);
		verStep = int(area.height() * scrollStep);
	}

	BPerch perch = boss->perch();
	if (autoBottom)
		perch.bottom() = boss->preferSize(Part_Bottom).height();
	if (autoRight)
		perch.right() = boss->preferSize(Part_Right).width();

	if (horScrollBar)
	{
		if (horScrollBar->parent() == boss && !horScrollBar->fettered())
		{
			if (!horScrollHint || areaAlign & Align_Horizontal)
			{
				horScrollBar->setVisible(false);
			}
			else if (horScrollHint < ScrollHint_Max)
			{
				int height = bMax(handleSize.height(), horScrollBar->sizeHint().height());
				horScrollBar->setHeight(height);
				if (horScrollHint == ScrollHint_Auto)
				{
					if (autoBottom && perch.bottom() < height)
						perch.bottom() = height;
					horScrollBar->setVisible(crect.width() < area.width());
				}
				if (horScrollHint == ScrollHint_Always)
				{
					if (autoBottom && perch.bottom() < height)
						perch.bottom() = height;
					horScrollBar->setVisible(true);
				}
			}
			horScrollBar->raise();
		}
		horScrollBar->setRange(0, area.width());
		horScrollBar->setPage(crect.width());
		horScrollBar->setStep(horStep);
		scrollbar_member(horScrollBar)->value = crect.left() - area.left();
		scrollbar_member(horScrollBar)->dirty = true;
	}

	if (verScrollBar)
	{
		if (verScrollBar->parent() == boss && !verScrollBar->fettered())
		{
			if (!verScrollHint || areaAlign & Align_Vertical)
			{
				verScrollBar->setVisible(false);
			}
			else if (verScrollHint <= ScrollHint_Max)
			{
				int width = bMax(handleSize.width(), verScrollBar->sizeHint().width());
				verScrollBar->setWidth(width);
				if (verScrollHint == ScrollHint_Auto)
				{
					if (autoRight && perch.right() < width)
						perch.right() = width;
					verScrollBar->setVisible(crect.height() < area.height());
				}
				if (verScrollHint == ScrollHint_Always)
				{
					if (autoRight && perch.right() < width)
						perch.right() = width;
					verScrollBar->setVisible(true);
				}
			}
			verScrollBar->raise();
		}
		verScrollBar->setParent(boss);
		verScrollBar->setRange(0, area.height());
		verScrollBar->setPage(crect.height());
		verScrollBar->setStep(verStep);
		scrollbar_member(verScrollBar)->value = crect.top() - area.top();
		scrollbar_member(verScrollBar)->dirty = true;
	}

	boss->setPerch(perch);

	if (horScrollBar && horScrollBar->parent() == boss && !horScrollBar->fettered())
	{
		if (horScrollHint == ScrollHint_Floating)
		{
			int height = horScrollBar->height();
			BRect brect = boss->rect(Part_Bottom);
			horScrollBar->setRect(brect.x(), brect.bottom() - height, brect.width(), height);
		}
		else
		{
			horScrollBar->setRect(boss->rect(Part_Bottom));
		}
	}

	if (verScrollBar && verScrollBar->parent() == boss && !verScrollBar->fettered())
	{
		if (verScrollHint == ScrollHint_Floating)
		{
			int width = verScrollBar->width();
			BRect rrect = boss->rect(Part_Right);
			verScrollBar->setRect(rrect.right() - width, rrect.y(), width, rrect.height());
		}
		else
		{
			verScrollBar->setRect(boss->rect(Part_Right));
		}
	}
}
void member_BScrollArea::applyScrolling()
{
	BRect crect = boss->centerRect();
	int xoffset = horScrollBar->value();
	int yoffset = verScrollBar->value();
	int x = crect.x() - xoffset;
	int y = crect.y() - yoffset;
	if (rawArea.pos() != BPoint(x, y))
	{
		rawArea.setPos(x, y);
		adjustRealArea();
		boss->scrolling(Orientation_Vertical);
		boss->fresh();
	}
}

void member_BScrollArea::slotMouseLeaveVerticalScrollBar()
{
	if (verScrollHint == ScrollHint_Floating)
	{
		verScrollBar->hide();
	}
}
void member_BScrollArea::slotVerticalScrollValueChanged(BObject* object, const BValue& value)
{
	applyScrolling();
}

void member_BScrollArea::slotMouseLeaveHorizontalScrollBar()
{
	if (horScrollHint == ScrollHint_Floating)
	{
		horScrollBar->hide();
	}
}
void member_BScrollArea::slotHorizontalScrollValueChanged(BObject* object, const BValue& value)
{
	applyScrolling();
}

